# -*- coding: utf-8 -*-
#    Copyright (C) 2012  Ruslan V. Us <unclerus@gmail.com>, Aleksandr V. Bannov <kokudzin@gmail.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os, sys, atexit, time, _log
from signal import SIGTERM

class BaseDaemon (object):

    def __init__ (self, pidFile, **kwargs):
        self._stdin = kwargs.get ('stdin')
        self._stdout = kwargs.get ('stdout')
        self._stderr = kwargs.get ('stderr')
        self._pidFile = pidFile
        self._log = _log.Log ('daemon', 'daemon.log')
    
    def _fork (self):
        try:
            if os.fork () > 0:
                sys.exit (0)
        except OSError, e:
            sys.stderr.write ('fork failed: {0} ({1})\n'.format (e.errno, e.strerror))
            sys.exit (1)

    def _done (self):
        self.done ()
        os.remove (self._pidFile)
    
    def _getPid (self):
        try:
            pf = file (self._pidFile, 'r')
            pid = int (pf.read ().strip ())
            pf.close ()
        except IOError:
            pid = None
        return pid

    def _daemonize (self):
        self._fork ()

        os.chdir ('/')
        os.setsid ()
        os.umask (0)

        self._fork ()
        if self._stdin:
            os.dup2 (file (self._stdin, 'r').fileno (), sys.stdin.fileno ())
        if self._stdout:
            sys.stdout.flush ()
            fd = os.open (self._stdout, os.O_RDWR|os.O_CREAT)
            os.dup2 (fd, sys.stdout.fileno ())
        if self._stderr:
            sys.stderr.flush ()
            fd = os.open (self._stderr, os.O_RDWR|os.O_CREAT)
            os.dup2 (fd, sys.stderr.fileno ())

        atexit.register (self._done)
        pid = str (os.getpid ())
        with file (self._pidFile, 'w+') as f:
            f.write ('{0}\n'.format (pid))

    def start (self):
        self._log.notice ('starting...')
        if self._getPid ():
            sys.stderr.write ('pidFile {0} already exist. Daemon already running?\n'.format (self._pidFile))
            sys.exit (1)
        self._daemonize ()
        self.run ()

    def stop (self):
        pid = self._getPid ()
        if not pid:
            sys.stderr.write ('pidFile {0} does not exist. Daemon not running?\n'.format (self._pidFile))
            return
        try:
            while True:
                os.kill (pid, SIGTERM)
                time.sleep (0.1)
        except OSError, err:
            err = str (err)
            if err.find ('No such process') > 0 and os.path.exists (self._pidFile):
                os.remove (self._pidFile)
            else:
                self._log.notice (err)
                sys.exit (1)
        self._log.notice ('stoped')

    def restart (self):
        self.stop ()
        self.start ()

    def run (self):
        pass
    
    def done (self):
        pass

